Skip to main content

02 泛型编程

C++ 语言

全面兼容 C 语言,又巧妙揉合了一些面向对象的编程理念。

  • 用引用解决指针的问题。
  • 用 namespace 解决名字空间冲突的问题。
  • 通过 try-catch 解决检查返回值编程的问题。
  • 用 class 解决对象的创建、复制、销毁的问题,从而可以达到在结构体嵌套时可以深度复制的内存安全问题。
  • 通过重载操作符来达到操作上的泛型。
  • 通过模板 template 和虚函数的多态以及运行时识别来达到更高层次的泛型和多态。
  • 用 RAII、智能指针的方式,解决 C 语言中释放资源的代码。
  • 用 STL 解决 C 语言中算法和数据结构的坑。

C++ 泛型编程

算法应是和数据结构以及类型无关的。对于泛型的抽象,数据类型符合通用算法。

C++ 解决程序泛型问题的三个要点:

通过类的方式解决

  • 类的构造函数表示这个类的分配和释放。
  • 拷贝构造函数表示对内存的复制。
  • 重载操作符。

通过模板达到类型和算法的妥协

  • 模板的特化会根据使用者的类型在编译时期生成那个模板的代码。
  • 模板可以通过一个虚拟类型来做类型绑定,不会导致类型转换时的问题。

通过虚函数和运行时类型识别

  • 虚函数的多态在语义上可以支持同一类的类型泛型。
  • 运行时类型识别可以在泛型时对具体类型的特殊处理。

泛型编程的示例

Search 函数

做得泛型和通用,返回找到的这个元素的指针(地址)。

  1. 使用模板技术抽象类型,写出类型无关的数据结构(数据容器);
  2. 使用迭代器遍历或操作数据结构内的元素。
template<typename T, typename Iter>
Iter search(Iter pStart, Iter pEnd, T target)
{
for(Iter p = pStart; p != pEnd; p++) {
if ( *p == target )
return p;
}
return NULL;
}
  • typename T抽象数据结构中存储数据的类型。
  • typename Iter不同的数据结构需要自己实现迭代器,抽象掉不同类型的数据结构。
  • 对数据容器的遍历使用Iter中的++方法,数据容器重载操作符,通过操作符重载泛型掉了遍历。
  • 入参使用pStart和pEnd表示遍历的起止。
  • *Iter取得指针的内容,通过重载 * 取值操作符达到泛型。

Sum 函数

long sum(int *a, size_t size) {
long result = 0;
for(int i=0; i<size; i++) {
result += a[i];
}
return result;
}
template<typename T, typename Iter>
T sum(Iter pStart, Iter pEnd) {
T result = 0;
for(Iter p=pStart; p!=pEnd; p++) {
result += *p;
}
return result;
}
  • 0假设了类型是int;
  • T假设 Iter 中出来的类型是T。

C++ 泛型编程的迭代器

  • 迭代器需要和容器在一起
  • 需要重载一些操作符
  • typedef一些类型,告诉容器内的数据的实际类型
  • begin()和end()的基本操作
  • pointer _ptr的内部指针来指向当前的数据
template <class Iter>
typename Iter::value_type
sum(Iter start, Iter end, T init) {
typename Iter::value_type result = init;
while (start != end) {
result = result + *start;
start++;
}
return result;
}